﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "CmdSelector.h"
#include "ActionModifyCreator.h"
#include "ActionModifyDefaultBehavior.h"
#include "ActionModifyInput.h"
#include "IActionModifyBehavior.h"
#include "IMenuItemContainerDefinition.h"
#include "ISelection.h"
#include "ISelectionChangeEvent.h"
#include "UiCommonDialog.h"
#include "UiDocumentViewUtils.h"
#include "CommandRegister.h"
#include "CommandIds.h"
#include "IGraphicsNodeReference.h"
#include "ICommandMenuItemDefinition.h"
#include "IGenericElement.h"
#include "CategoryDefine.h"
#include "IElementBasicInformation.h"
#include "../QtGui/IWallProperty.h"
#include "IDocument.h"
#include "IUiView.h"
#include "IUiDocument.h"
#include "DeleteUtils.h"
#include "IUserTransaction.h"
#include "IElementDeletionContext.h"
#include "ICommandInteractionEventArgs.h"
#include "CommandParameters.h"
#include "IInteractionEventManager.h"
#include "IPickEventArgs.h"
#include "IPick.h"
#include "IPickResult.h"
#include "IUiView.h"
#include "ILinkElementProxy.h"
#include "ILinkDocumentElement.h"
#include "IModelView.h"
#include "IDrawingPoint.h"
#include "ICommandBehavior.h"
#include "IPickEvent.h"

// gdmp
#ifdef UiAdapter_IMPL
#include "UiAdapter.h"
#pragma region 选择集合头文件
#include "GDFoundation/IElementSets.h"
#include "GDFoundation/ElementSetsUtils.h"
#pragma endregion
#endif // UiAdapter_IMPL

#include "ISnap.h"
#include "IHighlights.h"
#include "IPickCandidates.h"
#include "ElementId.h"
#include "ActionBase.h"
#include "IElementShapeHandle.h"
#include "IPickFilter.h"
#include "ICanvas.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h" 


using namespace gcmp;
using namespace Sample;

namespace Sample
{
    class DefaultBehavior : public gcmp::ActionModifyDefaultBehavior
    {
    public:
        virtual OwnerPtr<IMenuItemContainerDefinition>  PrepareContextMenu(gcmp::IUiView *pUIView) override final
        {
            OwnerPtr<IMenuItemContainerDefinition> opContextMenus = IMenuItemContainerDefinition::Create(L"Element", L"右键菜单");;
            auto &references = ISelection::Get()->GetGraphicsNodeReferences();
            if (references.size() == 0)
            {
                // 未选中图元时，显示该菜单
                OwnerPtr<ICommandMenuItemDefinition> opAttr =
                    ICommandMenuItemDefinition::Create(ID_CMD_ZOOM_FIT, ID_CMD_ZOOM_FIT, GBMP_TR(L"缩放匹配"), GBMP_TR(L"缩放匹配"), L":images/View/缩放匹配.png");

                opContextMenus->AddSubMenuItem(TransferOwnership(opAttr));
            }
#ifdef UiAdapter_IMPL
            {
                IPickCandidates* pIPickCandidates = IPickCandidates::Get();
                DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pIPickCandidates, L"pIPickCandidates无效", L"GDMP", L"2020-08-03");

                const OwnerPtr<gcmp::IPick>& pCurrentPick = pIPickCandidates->GetCurrentPick();
                DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pCurrentPick, L"pCurrentPick无效", L"GDMP", L"2020-08-03");

                const gcmp::GraphicsNodeReferenceOwnerPtrVector& pickCandidate = pCurrentPick->GetAllGraphicsNodeReferences();

                IUiDocument* pUiDoc = pUIView->GetUiDocument();
                DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pUiDoc, L"pUiDoc无效", L"GDMP", L"2020-08-03");

                IDocument* pDoc = pUiDoc->GetDbDocument();
                DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"pDoc为空", L"GDMP", L"2019-08-19");
                if (1 == pickCandidate.size())
                {
                    const gcmp::OwnerPtr<gcmp::IGraphicsNodeReference>& pPickCandidate0 = pickCandidate[0];
                    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pPickCandidate0, L"pPickCandidate0为空", L"GDMP", L"2020-08-04");
                    ElementId elementId = pPickCandidate0->GetElementId();
                    Sample::UiAdapter::ApplyContextMenuStrategy(pDoc, elementId, opContextMenus.get());
                }


            }
#endif
            return TransferOwnership(opContextMenus);
        }
        virtual bool OnLButtonDoubleClick(IUiView* pCurrentView, const gcmp::Vector3d& pos, const ElementId& selectedElementId) override
        {
            IDocument *pDocument = UiDocumentViewUtils::GetCurrentDocument();
            DBG_WARN_AND_RETURN_FALSE_UNLESS(pDocument, L"pDocument为空", L"GDMP", L"2021-01-20");
            IElement* pElement = pDocument->GetElement(selectedElementId);
            IGenericElement* pGenericElement = quick_cast<IGenericElement>(pElement);

            if (IsUseOldDoubleClick(pElement)) 
            {
                std::wstring commandName;
                if (ICommandBehavior* pCommandBehavior = pElement->GetCommandBehaviorFW())
                {
                    const ICommandBehaviorData* pCommandBehaviorData =
                        pCommandBehavior->GetCommandBehaviorData(ICommandBehavior::GetDoubleClickCommandType());
                    if (pCommandBehaviorData)
                    {
                        commandName = pCommandBehaviorData->GetCommandId();
                        if (!commandName.empty())
                        {
                            gcmp::CommandParameters params;
                            params[L"Vector3d_X"] = pos.X();
                            params[L"Vector3d_Y"] = pos.Y();
                            params[L"Vector3d_Z"] = pos.Z();
                            const gcmp::CommandParameters dataParams = pCommandBehaviorData->GetCommandParams();
                            for (auto iter = dataParams.begin(); iter != dataParams.end(); ++iter)
                            {
                                params.insert(std::make_pair(iter->first, iter->second));
                            }
                            ICommandManager::Get()->SendCommand(commandName, params);
                            return true;
                        }
                    }
                }
            }
            else 
            {
                // 对接平台新方式
                OwnerPtr<ICommandInteractionEventArgs> opArgs = ICommandInteractionEventArgs::Create(pElement);
                DBG_WARN_AND_RETURN_FALSE_UNLESS(opArgs, L"opArgs为空", L"GDMP", L"2021-01-20");
                opArgs->SetClickType(ClickType::DoubleClick);
                CommandParameters cmdParams;
                cmdParams[L"Vector3d_X"] = pos.X();
                cmdParams[L"Vector3d_Y"] = pos.Y();
                cmdParams[L"Vector3d_Z"] = pos.Z();
                opArgs->SetCommandParameters(cmdParams);

                IInteractionEventManager* pMgr = IInteractionEventManager::Get();
                DBG_WARN_AND_RETURN_FALSE_UNLESS(pMgr, L"pMgr为空", L"GDMP", L"2021-01-20");
                pMgr->Execute(opArgs.get());
                if (opArgs->IsExcuted())
                {
                    return true;
                }
            }
            return false;
        }

        bool IsKeyAndButtonPressed(int code) const
        {
            return ::GetKeyState(code) & 0x8000 ? true : false;
        }

        virtual bool OnKeyDown(IUiView* pCurrentView, int nChar) override
        {
            // ctrl + z,undo
            if (IsKeyAndButtonPressed(VK_CONTROL) && (0x5A == nChar || 0x7A == nChar))
            {
                gcmp::ICommandManager* pCommandManager = gcmp::ICommandManager::Get();
                DBG_WARN_AND_RETURN_FALSE_UNLESS(pCommandManager, L"断言`pCommandManager`失败", L"GDMP", L"2023-12-06");
                pCommandManager->SendCommand(ID_CMD_UNDO);
                return true;
            }

            if (nChar == VK_DELETE)
            {
                ISelection* pSel = ISelection::Get();
                if (pSel->IsEmpty())
                {
                    return true;
                }
                if (UiCommonDialog::ButtonType::Yes != UiCommonDialog::ShowMessageBox(L"GDMP", L"确定要删除所选图元么？", (int)(int)UiCommonDialog::ButtonType::Yes | (int)UiCommonDialog::ButtonType::No))
                {
                    return true;
                }
                std::vector<ElementId> ids;
                for (auto& node : pSel->GetGraphicsNodeReferences())
                {
                    IElement* pElement = pSel->GetDocument()->GetElement(node->GetElementId());
                    ids.push_back(node->GetElementId());
                }
                std::set<ElementId> idsToDelete;
                std::set<ElementId> toDeletedSet;
                std::set<ElementId> toNotifiedSet;
                IDocument* pDocument = pCurrentView->GetUiDocument()->GetDbDocument();
                OwnerPtr<IElementDeletionContext> opElementDeletionContext =
                    IElementDeletionContext::CreateElementDeletionContext(pDocument,
                        idsToDelete, toDeletedSet, toNotifiedSet);

                OwnerPtr<IUserTransaction> opUserTransaction = IUserTransaction::Create(pDocument, L"删除构件");
                std::vector<std::pair<ElementId, std::wstring>> pErrorMessages;
                bool bRes = DeleteUtils::DeleteElements(pDocument, opElementDeletionContext.get(), ids, &pErrorMessages);

                ISelection::Get()->Clear(pDocument);
                IPickCandidates::Get()->Clear();
                IHighlights::Get()->Clear();
                pCurrentView->GetCanvas()->Refresh();

                if (bRes)
                {
                    opUserTransaction->Commit();
                }
                else
                {
                    opUserTransaction->Rollback();
                    UiCommonDialog::ShowMessageBox(L"GDMP", L"删除失败！", (int)UiCommonDialog::ButtonType::OK);
                }
            }
            return true;
        }

#ifdef UiAdapter_IMPL
#pragma region 选择集合
        bool ModifyElement(const IUiView* pCurrentView, const ElementId& modifiedElementId, const gcmp::Vector3d& originPt, const gcmp::Vector3d& moveVector) override
        {
            // FLAG:选择集合 当选中的是集合时禁止移动
            gcmp::IDocument* pDocument = pCurrentView->GetModelView()->GetDocument();
            std::vector<gcmp::ElementId> elementIds{ modifiedElementId };
            std::vector<gcmp::ElementId> lstElementSetsBySelect = gdmp::ElementSetsUtils().GetRelationElementSets(pDocument, elementIds);
            if (!lstElementSetsBySelect.empty())
            {
                return false;
            }
            //如果未选择集合，则按照默认行为处理
            return ActionModifyDefaultBehavior::ModifyElement(pCurrentView,modifiedElementId,originPt,moveVector);
        }
#pragma endregion
#endif //UiAdapter_IMPL

    private:
        //处理双击点对象弹窗问题，平台的点对象实现时，没有注册到Uid_Tree里面
        //使用平台新的双击响应方式时，FindNearestParentHandler返回值为空
        //在平台处理之后，本次新增的代码可以删除
        bool IsUseOldDoubleClick(const gcmp::IElement* pElement)
        {
            DBG_WARN_AND_RETURN_FALSE_UNLESS(pElement, L"断言`pElement`失败", L"GDMP", L"2023-12-30");
            const IGenericElement* pGenericElement = quick_cast<IGenericElement>(pElement);
            if (pGenericElement)
            {
                const IDrawingPoint* pDrawingPoint = quick_cast<IDrawingPoint>(pGenericElement->GetExternalObject());
                if (pDrawingPoint)
                    return true;
            }
            return false;
        }
    };
}

// 默认ModifyAction中对近程捕捉过滤掉选择集中的图元
class DefaultPickFilterForLocalSnap : public gcmp::IPickFilter
{
public:
    virtual bool AllowElement(const ElementId& elementId) const override
    {
        IDocument* pDoc = UiDocumentViewUtils::GetCurrentDocument();
        if (!pDoc)
        {
            return false;
        }
        // 获得所有选择集的图形节点
        const gcmp::GraphicsNodeReferenceOwnerPtrSet& selectedElems = ISelection::Get()->GetGraphicsNodeReferences();
        for (auto it = selectedElems.begin(); it != selectedElems.end(); ++it)
        {
            // compareId是用于比较的图形节点的图元
            gcmp::ElementId compareId = (*it)->GetElementId();

            // 如果图元是夹点，那么获得夹点所属图元做比较
            IElementShapeHandle *pShapeHandle = quick_cast<IElementShapeHandle>(pDoc->GetElement(compareId));
            if (pShapeHandle && pShapeHandle->GetMasterId().IsValid())
            {
                compareId = pShapeHandle->GetMasterId();
            }

            // 如果捕捉的图元在选择集中，则不捕捉
            if (compareId == elementId)
            {
                return false;
            }
        }
        return true;
    }

    virtual bool AllowGraphicsNode(const IGraphicsNodeReference& graphicsNodeReference) const override
    {
        return true;
    }

    virtual bool SetPickTargetOption(IPickTarget* pickTarget) override
    {
        return true;
    }
};

REGISTER_COMMAND(CmdSelector)

CmdSelector::CmdSelector()
    :gcmp::CommandBase(COMMAND_SELECT_SELECTOR)
{}

bool CmdSelector::IsEnabled() const
{
    gcmp::IDocument* pCurrentDoc = gcmp::UiDocumentViewUtils::GetCurrentDocument();

    return  (pCurrentDoc == nullptr) ? false : true;
}

gcmp::OwnerPtr<gcmp::IAction> CmdSelector::ExecuteCommand(const gcmp::CommandParameters& cmdParams)
{
    ActionModifyInput modifyActionInput;
    modifyActionInput.SetActionModifyBehavior(NEW_AS_OWNER_PTR(DefaultBehavior));
    modifyActionInput.SetPickFilterForLocalSnap(NEW_AS_OWNER_PTR(DefaultPickFilterForLocalSnap));

#ifdef UiAdapter_IMPL
    UiAdapter::ApplyPickPostProcessEventHandler(modifyActionInput);
#endif
    return TransferOwnership(ActionModifyCreator::Create(modifyActionInput));
}
